var PARAMETRO_velocidad_local_pesos = [1.0]; //Deben sumar 1.0
//Funcion decaida de velocidad local a velocidad promedio...
var PARAMETRO_inicio_peso_local = 0.0; // 0.9
var PARAMETRO_final_peso_local = 0.0;  // 0.1
var PARAMETRO_tiempo_decaida = 20.0; //Tiempo que toma en cambiar de velocidad actual a promedio
//para iniciar con velocidad promedio...
var PARAMETRO_coords_min_local = 3;
//Zona donde se rechazan coordenadas lejanas a la ruta
var PARAMETRO_rechazo_min = 0.05;
var PARAMETRO_rechazo_max = 0.95;
var PARAMETRO_rechazo_dist = 0.100;
//Maxima velocidad 
var PARAMETRO_velocidad_max = 120.0;   //Maxima velocidad local
var PARAMETRO_velocidad_min = 20.0;   //Minima velocidad local
var PARAMETRO_promedio_vel_max = 90.0; //Maxima velocidad promedio para un segmento
var PARAMETRO_velocidad_default = 50.0; //Velocidad Default


//Funciones auxiliares para prediccion de ruta...
function deg2rad(deg) {
	return deg * (Math.PI/180)
}

function pred_tiempo_texto(segundos){
	if (segundos < 10){
		return "En parada";
	} else if (segundos < 30) {
		return "Menos de 30 segundos";
	} else if (segundos < 60) {
		return "Menos de 1 minuto";
	} else {
		var minutos = Math.floor((segundos * 2) / 60) / 2.0;
		
		if (minutos >= 15){
			return "mas de 15 minutos";
		} else {
			if (minutos >= 2){
				return "" + minutos + " minutos";
			} else {
				return "mas de un minuto";
			}
		}
	}
}

function dateStringToSeconds(date_string){
	var tempo_date = new Date(date_string.substring(0, 10));
	tempo_date.setHours(parseInt(date_string.substring(11, 13)));
	tempo_date.setMinutes(parseInt(date_string.substring(14, 16)));
	tempo_date.setSeconds(parseInt(date_string.substring(17, 19)));
	
	return tempo_date.getTime() / 1000.0;
}

function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
	var R = 6371; // Radius of the earth in km
	var dLat = deg2rad(lat2 - lat1);  // deg2rad below
	var dLon = deg2rad(lon2 - lon1); 
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon/2) * Math.sin(dLon/2); 
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 
	var d = R * c; // Distance in km
		
	return d;
}

function obtener_porcentajes_ruta(coordenadas_ruta){
	var distancias = [];
	var i, lat1, lat2, lon1, lon2;
	var distancia_total = 0.0;
	for (i = 0; i < coordenadas_ruta.length - 1; i++){
		lat1 = coordenadas_ruta[i].latitud;
		lon1 = coordenadas_ruta[i].longitud;
		lat2 = coordenadas_ruta[i + 1].latitud;
		lon2 = coordenadas_ruta[i + 1].longitud;
		
		distancias[i] = getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2);
		distancia_total += distancias[i];
	}
	
	var porcentajes = [];
	porcentajes[0] = 0.0;
	var suma_actual = 0.0;
	for (i = 0; i < coordenadas_ruta.length - 1; i++){
		suma_actual += distancias[i];
		porcentajes[i + 1] = suma_actual / distancia_total;
	}
	
	return porcentajes;
}

function medirDistanciaRuta(coordenadas_ruta, posicion1, posicion1_extra, posicion2, posicion2_extra){
	if (posicion2 < posicion1 || 
		((posicion2 == posicion1) && (posicion2_extra <= posicion1_extra)) ){
		return 0.0;
	}
	
	var range_start;
	var range_end;
	var range_val;
	var total_distancia = 0.0;
	var lat1;
	var lon1;
	var lat2;
	var lon2;
	for (var i = posicion1; i <= posicion2; i++){
		if (i == coordenadas_ruta.length - 1){
			//No hay mas coordenadas....
			break;
		}
	
		range_start = ( i == posicion1 ) ? posicion1_extra : 0.0;
		range_end = ( i == posicion2 ) ? posicion2_extra : 1.0;		
		range_val = range_end - range_start;
				
		lat1 = coordenadas_ruta[i].latitud;
		lon1 = coordenadas_ruta[i].longitud;
		lat2 = coordenadas_ruta[i + 1].latitud;
		lon2 = coordenadas_ruta[i + 1].longitud;
		total_distancia += getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) * range_val;
	}
	
	return total_distancia;
}

function punto_cercano_ruta_abs(x_lat, x_lon, coordenadas_ruta){
	var punto_cercano_lat = null;
	var punto_cercano_lon = null;
	var punto_cercano_tfact = null;
	var punto_cercano_idx = null;
	var distancia_cercana = null;
	var xp_lat = 0.0;
	var xp_lon = 0.0;
	var t_fact = 0.0;
	var t_dist = 0.0;
	var i;
	x_lat = parseFloat(x_lat);
	x_lon = parseFloat(x_lon);
	
	//para cada punto de la ruta...
	//(tratarles como valores X, Y)
	for (i = 0; i < coordenadas_ruta.length - 1; i++){
		//calcular proyeccion...
		//t = dot(X - P1, P2 - P1) / POW(|P2 - P1|, 2)
		//usar t para aproximar!!! Xp = t * (P2 - P1) + P1
		
		//Vector a proyectar...
		x_p1_lat = parseFloat(x_lat) - parseFloat(coordenadas_ruta[i].latitud);
		x_p1_lon = parseFloat(x_lon) - parseFloat(coordenadas_ruta[i].longitud);
		
		//Vector en ruta...
		p2_p1_lat = parseFloat(coordenadas_ruta[i + 1].latitud) - parseFloat(coordenadas_ruta[i].latitud);
		p2_p1_lon = parseFloat(coordenadas_ruta[i + 1].longitud) - parseFloat(coordenadas_ruta[i].longitud);
		
		t_fact = (x_p1_lat * p2_p1_lat + x_p1_lon * p2_p1_lon) / (p2_p1_lat * p2_p1_lat + p2_p1_lon * p2_p1_lon);
		
		if (t_fact < 0.0){
			xp_lat = parseFloat(coordenadas_ruta[i].latitud);
			xp_lon = parseFloat(coordenadas_ruta[i].longitud);
			t_fact = 0.0;
		} else {
			if (t_fact > 1.0){
				xp_lat = parseFloat(coordenadas_ruta[i + 1].latitud);
				xp_lon = parseFloat(coordenadas_ruta[i + 1].longitud);
				t_fact = 1.0;
			} else {
				xp_lat = parseFloat(coordenadas_ruta[i].latitud) + t_fact * p2_p1_lat;
				xp_lon = parseFloat(coordenadas_ruta[i].longitud) + t_fact * p2_p1_lon;
			}
		}
		
		t_dist = getDistanceFromLatLonInKm(x_lat,x_lon,xp_lat,xp_lon);		
		
		//El mas cercano....
		if (distancia_cercana == null || distancia_cercana > t_dist){
			distancia_cercana = t_dist;
			punto_cercano_lat = xp_lat;
			punto_cercano_lon = xp_lon;
			punto_cercano_tfact = t_fact;
			punto_cercano_idx = i;
		}
	}
		
	return {
		"latitud": punto_cercano_lat, 
		"longitud": punto_cercano_lon, 
		"distancia": distancia_cercana, 
		"tfact": punto_cercano_tfact,
		"index": punto_cercano_idx,
	};	
}

function punto_cercano_ruta_rel(x_lat, x_lon, coordenadas_ruta, ultima_posicion, ultimo_factor, delta_tiempo){
	var punto_cercano_lat = null;
	var punto_cercano_lon = null;
	var punto_cercano_tfact = null;
	var punto_cercano_idx = null;
	var distancia_cercana = null;
	
	var xp_lat = 0.0;
	var xp_lon = 0.0;
	var t_fact = 0.0;
	var t_dist = 0.0;
	
	var i, lat1, lon1, lat2, lon2;
	var total_distancia = 0.0;
	var actual_factor = ultimo_factor;
	var velocidad_requerida;
	
	x_lat = parseFloat(x_lat);
	x_lon = parseFloat(x_lon);
	
	if (delta_tiempo <= 0.0){
		delta_tiempo = 0.001;
	}
	
	//para cada punto de la ruta...
	//(tratarles como valores X, Y)
	for (i = ultima_posicion; i < coordenadas_ruta.length - 1; i++){
		velocidad_requerida = total_distancia / (delta_tiempo / 3600.0);
		if (velocidad_requerida > PARAMETRO_promedio_vel_max){
			//No puede llegar tan lejos en el tiempo dado...
			break;
		}
		
		//Agregar la distancia actual a lo requerido...
		lat1 = coordenadas_ruta[i].latitud;
		lon1 = coordenadas_ruta[i].longitud;
		lat2 = coordenadas_ruta[i + 1].latitud;
		lon2 = coordenadas_ruta[i + 1].longitud;
		total_distancia += getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2)  * (1.0 - actual_factor);		
		
		//calcular proyeccion...
		//t = dot(X - P1, P2 - P1) / POW(|P2 - P1|, 2)
		//usar t para aproximar!!! Xp = t * (P2 - P1) + P1
		
		//Vector a proyectar...
		x_p1_lat = x_lat - parseFloat(coordenadas_ruta[i].latitud);
		x_p1_lon = x_lon - parseFloat(coordenadas_ruta[i].longitud);
		
		//Vector en ruta...
		p2_p1_lat = parseFloat(coordenadas_ruta[i + 1].latitud) - parseFloat(coordenadas_ruta[i].latitud);
		p2_p1_lon = parseFloat(coordenadas_ruta[i + 1].longitud) - parseFloat(coordenadas_ruta[i].longitud);
		
		t_fact = (x_p1_lat * p2_p1_lat + x_p1_lon * p2_p1_lon) / (p2_p1_lat * p2_p1_lat + p2_p1_lon * p2_p1_lon);
		
		if (t_fact < actual_factor){
			xp_lat = parseFloat(coordenadas_ruta[i].latitud) + actual_factor * p2_p1_lat;
			xp_lon = parseFloat(coordenadas_ruta[i].longitud) + actual_factor * p2_p1_lon;
			t_fact = actual_factor;
		} else {
			if (t_fact > 1.0){
				xp_lat = parseFloat(coordenadas_ruta[i + 1].latitud);
				xp_lon = parseFloat(coordenadas_ruta[i + 1].longitud);
				t_fact = 1.0;
			} else {
				xp_lat = parseFloat(coordenadas_ruta[i].latitud) + t_fact * p2_p1_lat;
				xp_lon = parseFloat(coordenadas_ruta[i].longitud) + t_fact * p2_p1_lon;
			}
		}
		actual_factor = 0.0;
		
		t_dist = getDistanceFromLatLonInKm(x_lat,x_lon,xp_lat,xp_lon);		
		
		//El mas cercano....
		if (distancia_cercana == null || distancia_cercana > t_dist){
			distancia_cercana = t_dist;
			punto_cercano_lat = xp_lat;
			punto_cercano_lon = xp_lon;
			punto_cercano_tfact = t_fact;
			punto_cercano_idx = i;
		}
				
	}
	
	return {
		"latitud": punto_cercano_lat, 
		"longitud": punto_cercano_lon, 
		"distancia": distancia_cercana, 
		"tfact": punto_cercano_tfact,
		"index": punto_cercano_idx,
	};	
}

function puntos_cercanos_coordenadas(arreglo_coordenadas, coordenadas_ruta){
	var puntos_mas_cercanos = [];
	var tempo_punto;
	for (var k = 0; k < arreglo_coordenadas.length; k++){
		tempo_punto = punto_cercano_ruta_abs(arreglo_coordenadas[k].latitud, arreglo_coordenadas[k].longitud, coordenadas_ruta);
		puntos_mas_cercanos.push(tempo_punto);
	}
	
	return puntos_mas_cercanos;
}

function extraer_velocidades_ruta(data_ruta){
	var velocidades_ruta = [];
	
	for (var i = 0; i < data_ruta.length; i++){
		velocidades_ruta[i] = data_ruta[i].velocidad_promedio;
	}
	
	return velocidades_ruta;
}

function calcular_velocidad_local(coordenadas_ruta, historial_posiciones){
	if (historial_posiciones.length < 2){
		return 0.0;
	}

	var k;
	var pesos = PARAMETRO_velocidad_local_pesos;
	var n_historicos = PARAMETRO_velocidad_local_pesos.length;
	var delta_tiempo;
	var delta_distancia;
	var tempo_velocidad;	

	var current_k = historial_posiciones.length - 1;
	var limit = (historial_posiciones.length - n_historicos >= 1) ? (historial_posiciones.length - n_historicos) : 1;
	
	var estimado_velocidad = 0.0;
	
	for (var k = current_k; k >= limit; k--){
		//Diferencia de tiempos...
		delta_tiempo = (dateStringToSeconds(historial_posiciones[current_k].fecha_hora_envio_servidor) - 
						dateStringToSeconds(historial_posiciones[k - 1].fecha_hora_envio_servidor));
		delta_distancia = 0.0;
		
		if ( delta_tiempo > 0){
			//Diferencia entre distancias...
			 delta_distancia = medirDistanciaRuta(coordenadas_ruta, historial_posiciones[k - 1].index, historial_posiciones[k - 1].factor, 
												historial_posiciones[current_k].index, historial_posiciones[current_k].factor);
			
			tempo_velocidad = delta_distancia / (delta_tiempo / 3600.0);
		} else {						
			tempo_velocidad = 0.0;
			delta_distancia = 0.0;
		}	
		
		estimado_velocidad += tempo_velocidad * pesos[current_k - k];
	}
	
	return estimado_velocidad;
}

function predecir_posicion_ruta(data_ruta, actual_posicion, actual_factor, velocidades_ruta, delta_tiempo, historial_posiciones){
	//Seguir ruta desde posicion inicial
	var posicion_calculada = actual_posicion;
	var factor_extra = actual_factor;
	
	var prediccion_posicion = actual_posicion;
	var prediccion_factor = actual_factor;	
	var prediccion_latitud = data_ruta[actual_posicion].latitud;
	var prediccion_longitud = data_ruta[actual_posicion].longitud;
	
	if ( posicion_calculada < data_ruta.length - 1){
		var tiempo_restante = delta_tiempo;
		
		var tiempo_transcurrido = 0.0;
		var factor_decaida;
		var peso_actual_local;
		
		var distancia_actual = 0.0;
		var tiempo_requerido = 0.0;
		var lat1, lon1, lat2, lon2;
		
		var velocidad_local = calcular_velocidad_local(data_ruta, historial_posiciones);
		var velocidad_historica;
		var velocidad_estimada;
		
		
		//Seguir cada punto considerando velocidad promedio....
		while (tiempo_restante > 0.0){			
			lat1 = data_ruta[posicion_calculada].latitud;
			lon1 = data_ruta[posicion_calculada].longitud;
			lat2 = data_ruta[posicion_calculada + 1].latitud;
			lon2 = data_ruta[posicion_calculada + 1].longitud;
			
			//Calcular tiempo para recorrer segmento (en segundos)....
			//Obtener distancia...
			distancia_actual = getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2);
						
			//Obtener velocidad....
			if (velocidades_ruta[posicion_calculada] == null){
				//Sistema no entrenado, usar promedio...
				velocidad_historica = PARAMETRO_velocidad_default;				
			} else {
				if (velocidades_ruta[posicion_calculada] > 0.0){
					velocidad_historica = velocidades_ruta[posicion_calculada];
				} else {
					velocidad_historica = PARAMETRO_velocidad_default;
				}
			}
						
			if (historial_posiciones.length > PARAMETRO_coords_min_local){
				//Usar combinacion lineal con factor de decaida....
				factor_decaida = (tiempo_transcurrido / PARAMETRO_tiempo_decaida);
				if (factor_decaida > 1.0){
					factor_decaida = 1.0
				}
				peso_actual_local = PARAMETRO_final_peso_local * factor_decaida + 
									PARAMETRO_inicio_peso_local * (1.0 - factor_decaida);

				velocidad_estimada = velocidad_historica * (1.0 - peso_actual_local) + 
									velocidad_local * peso_actual_local;			
			} else {
				//No suficientes coordenadas, usar velocidad promedio...
				velocidad_estimada = velocidad_historica;				
			}
			if (velocidad_estimada > PARAMETRO_velocidad_max){
				velocidad_estimada = PARAMETRO_velocidad_max;
			}
			if (velocidad_estimada <= 0.0){
				//No se movera....
				prediccion_posicion = posicion_calculada;
				prediccion_factor = 0.0;	
				prediccion_latitud = data_ruta[prediccion_posicion].latitud;
				prediccion_longitud = data_ruta[prediccion_posicion].longitud;
				break;
			}
			
			//Velocidad obtenida, medir tiempo requerido en segundos...
			tiempo_requerido = (distancia_actual / velocidad_estimada) * 3600.0;
			
			//Ajuste por seccion ya recorrida..
			if (factor_extra > 0.0){
				tiempo_requerido *= (1.0 - factor_extra);
			}
			
			if (tiempo_restante > tiempo_requerido){
				//Pasar a siguiente punto...
				tiempo_restante -= tiempo_requerido;
				tiempo_transcurrido += tiempo_requerido;
				posicion_calculada++;
				factor_extra = 0.0;
				
				if (posicion_calculada >= data_ruta.length - 1){				
					prediccion_posicion = posicion_calculada;
					prediccion_factor = 0.0;
					prediccion_latitud = data_ruta[prediccion_posicion].latitud;
					prediccion_longitud = data_ruta[prediccion_posicion].longitud;
					break;
				}				
			} else {				
				prediccion_posicion = posicion_calculada;
				
				//Interpolar coordenada...
				if ( factor_extra > 0.0){
					prediccion_factor = (tiempo_restante / tiempo_requerido) * (1.0 - factor_extra) + factor_extra;
				} else {
					prediccion_factor = (tiempo_restante / tiempo_requerido);
				}				
				
				prediccion_latitud = lat1 * (1.0 - prediccion_factor) + lat2 * prediccion_factor;
				prediccion_longitud = lon1 * (1.0 - prediccion_factor) + lon2 * prediccion_factor;
				
				tiempo_restante = 0.0;
			}			
		}		
	}
	
	return {
		"posicion": prediccion_posicion,
		"factor": prediccion_factor,
		"latitud": prediccion_latitud,
		"longitud": prediccion_longitud,
		"velocidad_local": velocidad_local,
	};
}

function predecir_tiempo_posicion(data_ruta, actual_posicion, actual_factor, velocidades_ruta, 
								  historial_posiciones, destino_posicion, destino_factor){
	
	//Seguir ruta desde posicion inicial
	var prediccion_posicion = actual_posicion;
	var prediccion_factor = actual_factor;
	
	var tiempo_total = 0.0;
	var factor_decaida;
	var peso_actual_local;
	
	var velocidad_local = calcular_velocidad_local(data_ruta, historial_posiciones);
	var distancia_actual = 0.0;
	var velocidad_estimada;	
	var tiempo_requerido = 0.0;
	var range_start, range_end, range_val;
	var lat1, lon1, lat2, lon2;
	
	while (prediccion_posicion + prediccion_factor <
			destino_posicion + destino_factor){	
		
		//Obtener distancia entre posicion predecida actual y siguiente posicion...
		lat1 = data_ruta[prediccion_posicion].latitud;
		lon1 = data_ruta[prediccion_posicion].longitud;
		lat2 = data_ruta[prediccion_posicion + 1].latitud;
		lon2 = data_ruta[prediccion_posicion + 1].longitud;
		
		//Acortar inicio en primer segmento
		range_start = (prediccion_posicion == actual_posicion) ? actual_factor : 0.0;
		//Acortar en final de ultimo segmento...
		range_end = (prediccion_posicion == destino_posicion) ? destino_factor : 1.0;
		//Porcentaje de segmento a recorrer....
		range_val = range_end - range_start;
		//Distancia final a recorrer en segmento...
		distancia_actual = getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) * range_val;
		
		//Obtener velocidad....
		if (velocidades_ruta[prediccion_posicion] == null){
			//Sistema no entrenado, usar promedio...
			velocidad_historica = PARAMETRO_velocidad_default;				
		} else {
			velocidad_historica = velocidades_ruta[prediccion_posicion];
		}
		
		//La velocidad en el modelo...
		if (historial_posiciones.length > PARAMETRO_coords_min_local){
			//Usar combinacion lineal con factor de decaida....
			factor_decaida = (tiempo_total / PARAMETRO_tiempo_decaida);
			if (factor_decaida > 1.0){
				factor_decaida = 1.0
			}
			peso_actual_local = PARAMETRO_final_peso_local * factor_decaida + 
								PARAMETRO_inicio_peso_local * (1.0 - factor_decaida);

			
			velocidad_estimada = velocidad_historica * (1.0 - peso_actual_local) + 
								velocidad_local * peso_actual_local;
		} else {
			//No suficientes coordenadas, usar velocidad promedio...
			velocidad_estimada = velocidad_historica;
		}
		if (velocidad_estimada <= PARAMETRO_velocidad_min){
			//USar velocidad promedio donde la velocidad es desconocida o muy baja
			velocidad_estimada = PARAMETRO_velocidad_min;
		}
		if (velocidad_estimada > PARAMETRO_velocidad_max){
			velocidad_estimada = PARAMETRO_velocidad_max;
		}
		
		//Calcular tiempo para recorrer segmento (en segundos)
		tiempo_requerido = (distancia_actual / velocidad_estimada) * 3600.0;
		tiempo_total += tiempo_requerido;
		
		//Ver si pasar a siguiente punto....
		if (prediccion_posicion < destino_posicion){
			//Pasar a siguiente punto...
			prediccion_posicion++;
			prediccion_factor = 0.0;
		} else {
			//Finalizar...
			prediccion_factor = destino_factor;
		}
	}
	
	return tiempo_total;	
}

function calcular_error_prediccion(data_ruta, original_data_gps, debug){
	if (original_data_gps.length == 0 || data_ruta.length == 0){
		return 0.0;
	}
	
	//Filtrar coordenadas recibidas fuera de tiempo...
	var i;
	var data_gps = [];
	var ultima_fecha = original_data_gps[0].fecha_hora_envio_servidor;
	data_gps.push(original_data_gps[0]);
	for (i = 1; i < original_data_gps.length; i++){
		if (ultima_fecha < original_data_gps[i].fecha_hora_envio_servidor){
			data_gps.push(original_data_gps[i]);
			ultima_fecha = original_data_gps[i].fecha_hora_envio_servidor;
		}
	};
	
	var error_total = 0.0;	
	var tiempo_inicial = dateStringToSeconds(data_gps[0].fecha_hora_envio_servidor);
	var porcentages_ruta = obtener_porcentajes_ruta(data_ruta);
	
	var punto_distancias = [];
	var punto_actual = punto_cercano_ruta_abs(data_gps[0].latitud, data_gps[0].longitud, data_ruta, 0.0);
	punto_distancias.push(punto_actual.distancia);
	
	var prediccion_posicion = punto_actual.index;
	var prediccion_factor = punto_actual.tfact;	
	var recorrido_posicion = punto_actual.index;
	var recorrido_factor = punto_actual.tfact;
	var velocidades_ruta = extraer_velocidades_ruta(data_ruta);
	var prediccion;
	var error_prediccion;
	
	var salida = "";	
	var distancia;
	if (debug){
		salida += "Coordenadas Ruta\n";
		salida += "Index\t Latitud\t Longitud\t Distancia Sig.\t \n";
		for (i = 0; i < data_ruta.length; i++){
			if ( i < data_ruta.length - 1){
				distancia = getDistanceFromLatLonInKm(data_ruta[i + 1].latitud, data_ruta[i + 1].longitud, 
													 data_ruta[i].latitud, data_ruta[i].longitud); 
				salida += i + "\t" + data_ruta[i].latitud + "\t" + data_ruta[i].longitud + "\t" + distancia.toFixed(6) + "\n"; 				
			} else {
				salida += i + "\t" + data_ruta[i].latitud + "\t" + data_ruta[i].longitud + "\n";				
			}
		}	
		
		salida += "Error GPS\n";
		salida += "Index\t ID\t Latitud\t Longitud\t Vel. Local\t Delta T\t Real Pos\t Real Factor\t Pred Pos\t Pred Factor\t Error\n";
	}	
	
	var historial_posiciones = [];
	historial_posiciones[0] = {
		"fecha_hora_envio_servidor": data_gps[0].fecha_hora_envio_servidor, 
		"index": punto_actual.index, 
		"factor": punto_actual.tfact
	};
	var cuenta_descartados = 0;
	var ultimo_tiempo = dateStringToSeconds(data_gps[0].fecha_hora_envio_servidor);
	var tiempo_actual;
	for (i = 1; i < data_gps.length; i++){
		//Siguiente coordenada...			
		
		//Obtener valor de la prediccion...
		//...Calcular diferencia de tiempo...		
		tiempo_actual = dateStringToSeconds(data_gps[i].fecha_hora_envio_servidor);
		delta_tiempo = (tiempo_actual - ultimo_tiempo);

		//...Obtener prediccion ....
		prediccion = predecir_posicion_ruta(data_ruta, recorrido_posicion, recorrido_factor, 
											velocidades_ruta, delta_tiempo, historial_posiciones);
		prediccion_posicion = prediccion.posicion;
		prediccion_factor = prediccion.factor;
		prediccion_v_local = prediccion.velocidad_local;
		
		//Obtener posicion real relativa...		
		//(la funcion asegura que solo se consideran puntos iguales o mayores  a la posicion de referencia
		//Tambien se dejan de considerar puntos que estan muy alejados de referencia
		punto_actual = punto_cercano_ruta_rel(data_gps[i].latitud, data_gps[i].longitud, data_ruta, 
											recorrido_posicion, recorrido_factor, delta_tiempo);							
		punto_distancias.push(punto_actual.distancia);
		//...verificar si es invalida...
		if (punto_actual.distancia > PARAMETRO_rechazo_dist &&
			porcentages_ruta[punto_actual.index] >= PARAMETRO_rechazo_min &&
			porcentages_ruta[punto_actual.index] <= PARAMETRO_rechazo_max){
			//Invalida, saltar coordenada....
			cuenta_descartados++;
			if (debug){
				salida += i + "\t" + data_gps[i].id_recorrido_realizado_historico + "\t" + data_gps[i].latitud + 
						"\t" + data_gps[i].longitud + "\tDescartada\n";
			}
			continue;
		} else {
			//Valida, actualizar tiempo....
			ultimo_tiempo = tiempo_actual;
		}
		
		
		//...Agregar coordenada a historial....
		historial_posiciones[historial_posiciones.length] = {
			"fecha_hora_envio_servidor": data_gps[i].fecha_hora_envio_servidor, 
			"index": punto_actual.index, 
			"factor": punto_actual.tfact
		};
		
		//Actualizar posicion ruta...
		recorrido_posicion = punto_actual.index;
		recorrido_factor = punto_actual.tfact;
		
		//Calcular el error entre ambas posiciones...
		if ( recorrido_posicion + recorrido_factor < prediccion_posicion + prediccion_factor){
			//Medir de posicion hasta prediccion
			
			error_prediccion = medirDistanciaRuta(data_ruta, recorrido_posicion, recorrido_factor, 
											prediccion_posicion, prediccion_factor);
													
		} else {
			//Medir desde prediccion hasta posicion real			
			error_prediccion = medirDistanciaRuta(data_ruta, prediccion_posicion, prediccion_factor, 
											recorrido_posicion, recorrido_factor);			
		}
		error_total += error_prediccion;
		
		if (debug){
			salida += i + "\t" + data_gps[i].id_recorrido_realizado_historico + "\t" + 
					parseFloat(data_gps[i].latitud).toFixed(5) + "\t" + parseFloat(data_gps[i].longitud).toFixed(5) + "\t" + 
					prediccion_v_local.toFixed(4) + "\t" + delta_tiempo + "\t" +
					recorrido_posicion + "\t" + recorrido_factor.toFixed(2) + "\t" + 
					prediccion_posicion + "\t" + prediccion_factor.toFixed(2) + "\t" + 
					error_prediccion.toFixed(6) + "\n";
		}		
	}
	salida += "Total Error:\t" + error_total + "\n";
	
	if (debug){
		var suma_distancias = 0.0;
		var min_p_distancia = punto_distancias[0];
		var max_p_distancia = punto_distancias[0];
		for (i = 0; i < punto_distancias.length; i++){
			suma_distancias += punto_distancias[i];
			if (punto_distancias[i] < min_p_distancia){
				min_p_distancia = punto_distancias[i];
			}
			if (punto_distancias[i] > max_p_distancia){
				max_p_distancia = punto_distancias[i];
			}
		}
		var promedio_distancias = suma_distancias / punto_distancias.length;
		
		salida += "Distancia aproximaciones\n";
		salida += "Promedio \tMinimo \tMaximo\n";
		salida += promedio_distancias + "\t" + min_p_distancia + "\t" + max_p_distancia + "\n";
		
		salida += "Descartadas\t" + cuenta_descartados + "\n";
	}
	
	return {
		"error_total": error_total,
		"salida": salida,
	};
}

function crear_nueva_prediccion(){
	return {
		id_recorrido: 0,
		last_server_coord: null,
		last_coord_id: null,
		delta_tiempo_extra: 0,
		ultimo_tiempo_coordenada: new Date(),
		tiempo_inicial_coordenada: new Date(),		
		sim_velocidad: 1.0,
		parada_actual: 0,
		parada_tiempo_siguiente: null,
		destino_tiempo: null,
		posicion_actual_ruta: 0,
		posicion_factor_extra: 0.0,
		prediccion_posicion: 0,
		prediccion_factor_extra: 0.0,
		prediccion_latitud: null,
		prediccion_longitud: null,
		historial_posiciones: [],
	};
}